import typing
import sys
import pathlib
import PySide2
from collections import deque
from PySide2.QtWidgets import (QApplication,QMainWindow,QHBoxLayout,QLabel,QListWidgetItem,
QAbstractItemView,QFileDialog,QAbstractItemView,QProgressBar,QDialog)
from PySide2.QtGui import QIcon,QCursor,QColor,QPalette
from PySide2.QtCore import Qt,Slot,QThread,QMutex,QMutexLocker,QTimerEvent,Signal
from PySide2.QtTextToSpeech import QTextToSpeech



from ui_findSN import Ui_MainWindow

class MyMain(QMainWindow):

    def __init__(
        self, parent: typing.Optional[PySide2.QtWidgets.QWidget] = None,
        flags: PySide2.QtCore.Qt.WindowFlags = Qt.WindowFlags()) -> None:
        super().__init__(parent, flags)

        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.ui.label.setText("")
        self.ui.lineEdit.setText("")
        self.sn_list = []
        self.__speaker = Speaker(self)
        self.__speaker.start()
        self.ui.action_add_sn.triggered.connect(self.add_text_to_sn)
        self.ui.action_clear_sn.triggered.connect(self.clear_sn)
        self.ui.lineEdit.returnPressed.connect(self.scan_event)
        self.ui.lineEdit.setAutoFillBackground(True)

    def add_text_to_sn(self) -> None:

        fname, fl = QFileDialog.getOpenFileName(self, "请选择SN文件", "", "text (*.txt)")
        if fname == "": return

        f = pathlib.Path(fname)
        self.sn_list.clear()
        with f.open() as fobj:
            for line in fobj:
                txt = line.strip()
                if txt in self.sn_list: continue
                self.sn_list.append(txt)
                self.ui.listWidget.addItem(QListWidgetItem(txt))
    
    def clear_sn(self) -> None:

        self.sn_list.clear()
        self.ui.listWidget.clear()
        self.ui.label.setText("")
        self.ui.lineEdit.setText("")
        self.ui.label.setStyleSheet("background-color: rgb(240, 240, 240);")

    def scan_event(self) -> None:

        text = self.ui.lineEdit.text()
        self.ui.label.setText(text)
        if text in self.sn_list:
            self.ui.label.setStyleSheet("background-color: rgb(0, 255, 0);")
            for index in range(self.ui.listWidget.count()):
                item = self.ui.listWidget.item(index)
                if item.text() == text:
                    self.__speaker.add_task("Yes")
                    item.setBackgroundColor(QColor(Qt.green))
                    self.ui.listWidget.scrollToItem(
                        item, QAbstractItemView.ScrollHint.PositionAtCenter)
        else:
            self.ui.label.setStyleSheet("background-color: rgb(255, 0, 0);")
            self.__speaker.add_task("No")
        self.ui.lineEdit.selectAll()
    
    def closeEvent(self, event: PySide2.QtGui.QCloseEvent) -> None:

        class th1(QThread):
            def __init__(self,
                callback,
                parent: typing.Optional[PySide2.QtCore.QObject] = None) -> None:
                super().__init__(parent)
                self.callback = callback
            
            def run(self) -> None:
                for n in range(50):
                    QThread.msleep(50)
                    try:
                        self.callback()
                    except RuntimeError:
                        break

        self.quit_dialog = ProgressWaitDialog(10, self)
        self.quit_dialog.set_progressbar_range(50)        
        th = th1(self.quit_dialog.add_progressbar_value)
        th.start()
        self.__speaker.finished.connect(self.quit_dialog.add_progressbar_value)
        self.__speaker.request_exit()
        self.quit_dialog.exec_()
        th.quit()
        th.wait()
        self.__speaker.wait()

        return super().closeEvent(event)

class ProgressWaitDialog(QDialog):

    def __init__(self,
        timeout:int = 0,
        parent: typing.Optional[PySide2.QtWidgets.QWidget] = None,
        f: PySide2.QtCore.Qt.WindowFlags = Qt.WindowFlags()|Qt.FramelessWindowHint
        ) -> None:
        super().__init__(parent, f)

        self._timeout = timeout
        self.__timeout_count = 0
        self.setAttribute(Qt.WA_DeleteOnClose, True)
        self.setAttribute(Qt.WA_TranslucentBackground, True)
        self.label = QLabel("请稍等...", self)
        self.progressbar = QProgressBar(self)
        self.progressbar.setWindowOpacity(0.6)
        self.progressbar.setValue(0)
        self.progressbar.setAlignment(Qt.AlignCenter)
        self.progressbar.setStyleSheet(
            """QProgressBar{
                    border: 2px solid grey;
                    border-radius: 5px;
                }
                QProgressBar::chunk{
                    background-color: red;
                    width: 10px;
                }
        """)
        self.hlayout = QHBoxLayout(self)
        self.progressbar.setWindowModality(Qt.WindowModal)
        self.hlayout.addWidget(self.label)
        self.hlayout.addWidget(self.progressbar)
        self.__progress_value_check_tid = self.startTimer(500, Qt.TimerType.VeryCoarseTimer)
        if self._timeout > 0:
            self.__timeout_tid = self.startTimer(1000, Qt.TimerType.VeryCoarseTimer)
    
    def timerEvent(self, event: QTimerEvent) -> None:
        
        if self._timeout > 0:
            if self.__timeout_count >= self._timeout:
                if self.__timeout_tid == event.timerId():
                    self.killTimer(self.__progress_value_check_tid)
                    self.killTimer(self.__timeout_tid)
                    self.reject()
            else:
                self.__timeout_count += 1

        if self.__progress_value_check_tid == event.timerId():
            if self.progressbar.value() == self.progressbar.maximum():
                self.killTimer(self.__progress_value_check_tid)
                self.accept()

    def set_progressbar_range(self, maxn:int, minn:int=0) -> None:

        self.progressbar.setMaximum(maxn)
        self.progressbar.setMinimum(minn)
    
    def set_progressbar_value(self, v:int) -> None:

        self.progressbar.setValue(v)
    
    def add_progressbar_value(self) -> None:

        self.progressbar.setValue(self.progressbar.value()+1)

    def set_label_text(self, text:str) -> None:

        self.label.setText(text)

class Speaker(QThread):

    def __init__(self, parent: typing.Optional[PySide2.QtCore.QObject] = None) -> None:
        super().__init__(parent)
        self.__task_queue = deque(maxlen=100)
        self.__speak_locker = QMutexLocker(QMutex(QMutex.RecursionMode.NonRecursive))
        self.__exit:bool = False
        self.__speak = QTextToSpeech(self)
        self.__speak.setVolume(1.0)
        for n in self.__speak.availableLocales():
            if n.name() == "en_US":
                self.__speak.setLocale(n)
                break
    
    def run(self) -> None:
        
        while True:
            try:
                with self.__speak_locker:
                    text = self.__task_queue.pop()
                self.__speak.say(text)
            except IndexError:
                QThread.usleep(10)
            if self.__exit: break
        self.__speak_locker.unlock()

    def request_exit(self) -> None:

        self.__exit = True
        
    def add_task(self, task:str) -> None:

        with self.__speak_locker:
            self.__task_queue.append(task)
    


# 0100056440
if __name__ == "__main__":

    app = QApplication(sys.argv)

    m = MyMain()
    m.show()
    sys.exit(app.exec_())